/**
  ******************************************************************************
  * File Name          : ctrl_shl.c
  * Description        : Code for data process applications
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "route.h"

static uint8_t decode_ackey[16][32] = {
  {0},
  
  /* 网关密钥 1 */
  {0x33, 0x11, 0xB1, 0x15, 0xED, 0xC3, 0x69, 0xB5, 0x7F, 0x15,
   0xC1, 0xA9, 0x69, 0xA3, 0x6F, 0xDB, 0x59, 0x57, 0x43, 0x75,
   0x77, 0x61, 0xDD, 0x51, 0xF1, 0xBB, 0x63, 0xC7, 0xF1, 0xE5, 0xE3, 0xE5},
  
  {0},
  {0},
  {0},
  
  /* 环控密钥 5 */
  {0x47, 0x0F, 0xCF, 0xE9, 0xC3, 0x87, 0x5B, 0x5D, 0xB7, 0xD1, 
   0x2F, 0x8B, 0xFB, 0xBD, 0x8B, 0x17, 0x03, 0xAF, 0x8D, 0x21, 
   0xCB, 0x19, 0x65, 0xCD, 0xF5, 0xF7, 0x59, 0x47, 0x77, 0xD3, 0xD7, 0xDD},
  
  {0},
  
  /* 小窗密钥 7 */
  {0x8F, 0x01, 0x6B, 0x03, 0x33, 0x65, 0x21, 0x05, 0x75, 0x51, 
   0xB1, 0x65, 0x87, 0x2F, 0x05, 0x13, 0x69, 0xCB, 0xBD, 0xA3, 
   0xE1, 0x71, 0x3F, 0xFB, 0xBF, 0x0F, 0x77, 0x67, 0xBB, 0x6D, 0x91, 0xC1}, 
  
  /* 料线密钥 8 */
  {0x31, 0xCD, 0x03, 0xAB, 0xB1, 0x37, 0x13, 0x69, 0x7D, 0xE3, 
   0x2B, 0x7D, 0xEB, 0xA9, 0xBF, 0x85, 0x13, 0x9F, 0xC9, 0xA7, 
   0x07, 0x07, 0x33, 0x3D, 0x5D, 0xFD, 0x21, 0x71, 0x5B, 0x49, 0xF5, 0x8B},
  
  /* 粥料器 9 */
  {0xBD, 0x1D, 0xC9, 0xE3, 0x45, 0x2B, 0xF3, 0x65, 0x2D, 0x45, 
   0x49, 0xC1, 0xA9, 0x29, 0xA9, 0xAB, 0xE5, 0x25, 0xCF, 0x13, 
   0xA9, 0x65, 0xA7, 0x7D, 0x89, 0x83, 0xA5, 0xAD, 0xB7, 0x63, 0x43, 0xED},
  
  /* 除粪密钥 10 */
  {0x9B, 0x33, 0xAD, 0x75, 0x05, 0x33, 0x8D, 0x3D, 0x8F, 0x75, 
   0x37, 0xC3, 0x31, 0x8D, 0x41, 0x73, 0x13, 0x4F, 0x85, 0xE1, 
   0xA3, 0xA3, 0xB9, 0x8D, 0x8F, 0x11, 0x09, 0x4D, 0x0F, 0xD1, 0xBD, 0x23},
  
  /* 视频监控 11 */
  {0xB1, 0x97, 0xB9, 0xCF, 0x13, 0xD1, 0x3D, 0x41, 0x81, 0x9B, 
   0xED, 0x5B, 0x69, 0xB5, 0x7D, 0x43, 0x59, 0x59, 0x49, 0x97, 
   0x3B, 0x09, 0xE7, 0xDF, 0x93, 0x65, 0x13, 0xD7, 0x27, 0x2F, 0x87, 0x35},
  
  /* 饲喂系统 12 */
  {0xFB, 0xFD, 0x9D, 0xF7, 0x13, 0xDD, 0xE1, 0x17, 0xB3, 0xF9, 
   0xED, 0x4D, 0x75, 0x6B, 0x4F, 0x03, 0xA1, 0x3B, 0x3B, 0x1F, 
   0xE7, 0x75, 0x35, 0x31, 0x6B, 0x0D, 0xDB, 0xC3, 0xAF, 0x7D, 0xBD, 0x0B},
  
  /* 水电用量 13 */ 
  {0x2B, 0x55, 0x19, 0x59, 0xF3, 0xDD, 0x55, 0x99, 0x1D, 0x41, 
   0xF7, 0xBD, 0xD3, 0x71, 0xFD, 0x0B, 0x6F, 0x2B, 0x8B, 0x07, 
   0xF7, 0x95, 0xA1, 0x1B, 0x7D, 0xE5, 0xB1, 0xB3, 0xE3, 0x55, 0x1B, 0x8F},
  
  /* 灯控密钥 14 */
  {0xEF, 0xA5, 0x59, 0x07, 0x7F, 0xD3, 0x5B, 0xF7, 0xCF, 0xE3, 
   0x35, 0x97, 0x07, 0x35, 0x2B, 0x95, 0x57, 0x35, 0xC9, 0xCB, 
   0x71, 0xC9, 0xDD, 0xA3, 0xB9, 0x5F, 0xCB, 0xC5, 0xD5, 0x1D, 0x2B, 0xCB},
  
  {0},
};

static const char base64_enc_map[64] =
{
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
	'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
	'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
	'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
	'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
	'8', '9', '+', '/'
};

typedef struct
{
  uint32_t a;
  uint32_t b;
  uint32_t  c;
}ft_variate_type;
static ft_variate_type ft_variate = {0};
static uint32_t ft(int t, uint32_t x, uint32_t y, uint32_t z)
{
  memset(&ft_variate, 0, sizeof(ft_variate_type));
  
	if(t < 20)
	{
		ft_variate.a = x & y;
		ft_variate.b = (~x) &z;
		ft_variate.c = ft_variate.a ^ ft_variate.b;
	}
	else if(t < 40)
	{
		ft_variate.c = x ^ y ^ z;
	}
	else if(t < 60)
	{
		ft_variate.a = x & y;
		ft_variate.b = ft_variate.a ^ (x & z);
		ft_variate.c = ft_variate.b ^ (y & z);
	}
	else if(t < 80)
	{
		ft_variate.c = (x ^ y) ^z;
	}

	return ft_variate.c;
}

static uint32_t k(int t)
{
  uint32_t c;

	if(t < 20)
	{
		c = 0x5a827999;
	}
	else if(t < 40)
	{
		c = 0x6ed9eba1;
	}
	else if(t < 60)
	{
		c = 0x8f1bbcdc;
	}
	else if(t < 80)
	{
		c = 0xca62c1d6;
	}

	return c;
}

typedef struct
{
  uint32_t c;
  uint32_t d;
  uint32_t e;
  uint32_t f;
  uint32_t g;
}rotl_variate_type;

static rotl_variate_type rotl_variate = {0};
static uint32_t rotl(int bits, uint32_t a)
{
  memset(&rotl_variate, 0, sizeof(rotl_variate_type));
  
	rotl_variate.c = (0x0001 << (32 - bits)) - 1;
	rotl_variate.d = ~rotl_variate.c;
	rotl_variate.e = (a & rotl_variate.c) << bits;
	rotl_variate.f = (a & rotl_variate.d) >> (32 - bits);
	rotl_variate.g = rotl_variate.e | rotl_variate.f;

	return (rotl_variate.g & 0xffffffff);
}

typedef struct
{
  uint32_t a;
  uint32_t b;
  uint32_t c;
  uint32_t d;
  uint32_t e;
  uint32_t h[5];
  uint32_t w[80];
  uint32_t num_blocks;
	uint32_t block_remainder;
	uint32_t padded_length;
  uint32_t l;
  uint32_t temp;
}sha1_variate_type;
static sha1_variate_type sha1_variate = {0};

static void sha1(uint8_t *message, int message_length, uint8_t *digest)
{
	uint32_t i;
  uint32_t t;
  
  memset(&sha1_variate, 0, sizeof(sha1_variate_type));
  
	/* Calculate the number of 512 bit blocks */
	sha1_variate.padded_length = message_length + 9; /* Add length for l */
	sha1_variate.l = message_length * 8;
	sha1_variate.num_blocks = sha1_variate.padded_length / 64;
	sha1_variate.block_remainder = sha1_variate.padded_length % 64;

	if(sha1_variate.block_remainder > 0)
	{
		sha1_variate.num_blocks++;
	}

//	padded_length = padded_length + (64 - block_remainder);

//	for(i = message_length; i < (num_blocks * 64); i++)
//	{
//		message[i] = 0x00;
//	}

	message[message_length] = 0x80;
	message[(sha1_variate.num_blocks * 64) - 1] = (uint8_t)(sha1_variate.l        & 0xff);
	message[(sha1_variate.num_blocks * 64) - 2] = (uint8_t)((sha1_variate.l >> 8)  & 0xff);
	message[(sha1_variate.num_blocks * 64) - 3] = (uint8_t)((sha1_variate.l >> 16) & 0xff);
	message[(sha1_variate.num_blocks * 64) - 4] = (uint8_t)((sha1_variate.l >> 24) & 0xff);
  
	sha1_variate.h[0] = 0x67452301;
	sha1_variate.h[1] = 0xefcdab89;
	sha1_variate.h[2] = 0x98badcfe;
	sha1_variate.h[3] = 0x10325476;
	sha1_variate.h[4] = 0xc3d2e1f0;

	for(i = 0; i < sha1_variate.num_blocks; i++)
	{
		for(t = 0; t < 80; t++)
		{
			if(t < 16)
			{
				sha1_variate.w[t]  = (256 * 256 * 256) * message[(i * 64) + (t * 4)];
				sha1_variate.w[t] += (256 * 256) * message[(i * 64) + (t * 4) + 1];
				sha1_variate.w[t] += (256) * message[(i * 64) + (t * 4) + 2];
				sha1_variate.w[t] += message[(i * 64) + (t * 4) + 3];
			}
			else if(t < 80)
			{
				sha1_variate.w[t] = rotl(1, (sha1_variate.w[t - 3] ^ sha1_variate.w[t - 8] ^ sha1_variate.w[t - 14] ^ sha1_variate.w[t - 16]));
			}
		}

		/* Initialize the five working variables */
		sha1_variate.a = sha1_variate.h[0];
		sha1_variate.b = sha1_variate.h[1];
		sha1_variate.c = sha1_variate.h[2];
		sha1_variate.d = sha1_variate.h[3];
		sha1_variate.e = sha1_variate.h[4];

		/* iterate a-e 80 times */
		for(t = 0; t < 80; t++)
		{
			sha1_variate.temp = (rotl(5, sha1_variate.a) + ft(t, sha1_variate.b, sha1_variate.c, sha1_variate.d)) & 0xffffffff;
			sha1_variate.temp = (sha1_variate.temp + sha1_variate.e) & 0xffffffff;
			sha1_variate.temp = (sha1_variate.temp + k(t)) & 0xffffffff;
			sha1_variate.temp = (sha1_variate.temp + sha1_variate.w[t]) & 0xffffffff;
			sha1_variate.e = sha1_variate.d;
			sha1_variate.d = sha1_variate.c;
			sha1_variate.c = rotl(30, sha1_variate.b);
			sha1_variate.b = sha1_variate.a;
			sha1_variate.a = sha1_variate.temp;
		}

		/* compute the ith intermediate hash value */
		sha1_variate.h[0] = (sha1_variate.a + sha1_variate.h[0]) & 0xffffffff;
		sha1_variate.h[1] = (sha1_variate.b + sha1_variate.h[1]) & 0xffffffff;
		sha1_variate.h[2] = (sha1_variate.c + sha1_variate.h[2]) & 0xffffffff;
		sha1_variate.h[3] = (sha1_variate.d + sha1_variate.h[3]) & 0xffffffff;
		sha1_variate.h[4] = (sha1_variate.e + sha1_variate.h[4]) & 0xffffffff;
	}

	digest[3]  = (uint8_t)(sha1_variate.h[0]       & 0xff);
	digest[2]  = (uint8_t)((sha1_variate.h[0] >> 8) & 0xff);
	digest[1]  = (uint8_t)((sha1_variate.h[0] >> 16) & 0xff);
	digest[0]  = (uint8_t)((sha1_variate.h[0] >> 24) & 0xff);
	digest[7]  = (uint8_t)(sha1_variate.h[1]       & 0xff);
	digest[6]  = (uint8_t)((sha1_variate.h[1] >> 8) & 0xff);
	digest[5]  = (uint8_t)((sha1_variate.h[1] >> 16) & 0xff);
	digest[4]  = (uint8_t)((sha1_variate.h[1] >> 24) & 0xff);
	digest[11] = (uint8_t)(sha1_variate.h[2]       & 0xff);
	digest[10] = (uint8_t)((sha1_variate.h[2] >> 8) & 0xff);
	digest[9]  = (uint8_t)((sha1_variate.h[2] >> 16) & 0xff);
	digest[8]  = (uint8_t)((sha1_variate.h[2] >> 24) & 0xff);
	digest[15] = (uint8_t)(sha1_variate.h[3]       & 0xff);
	digest[14] = (uint8_t)((sha1_variate.h[3] >> 8) & 0xff);
	digest[13] = (uint8_t)((sha1_variate.h[3] >> 16) & 0xff);
	digest[12] = (uint8_t)((sha1_variate.h[3] >> 24) & 0xff);
	digest[19] = (uint8_t)(sha1_variate.h[4]       & 0xff);
	digest[18] = (uint8_t)((sha1_variate.h[4] >> 8) & 0xff);
	digest[17] = (uint8_t)((sha1_variate.h[4] >> 16) & 0xff);
	digest[16] = (uint8_t)((sha1_variate.h[4] >> 24) & 0xff);
}

typedef struct
{
  uint8_t k0[64+4];
  uint8_t k0xorIpad[64+4];
  uint8_t step7data[64+4];
  uint8_t step5data[64+80+128];
  uint8_t step8data[64+20+128];
}hmac_sha1_variate_type;
static hmac_sha1_variate_type hmac_sha1_variate = {0};

static void hmac_sha1(const uint8_t *key, int key_length, char *dat, int data_length, uint8_t *digest)
{
	int i;
  
  if(data_length > 80)
  {
    return;
  }
  
  if(key_length > 64)
  {
    return;
  }
  
  memset(&hmac_sha1_variate, 0, sizeof(hmac_sha1_variate_type));
  
  /* Step 3 */
  memcpy(hmac_sha1_variate.k0, key, key_length);

	/* Step 4 */
	for(i = 0; i < 64; i++)
	{
		hmac_sha1_variate.k0xorIpad[i] = hmac_sha1_variate.k0[i] ^ 0x36;
	}

	/* Step 5 */
  memcpy(hmac_sha1_variate.step5data, hmac_sha1_variate.k0xorIpad, 64);

  memcpy(hmac_sha1_variate.step5data+64, dat, data_length);

	/* Step 6 */
	sha1(hmac_sha1_variate.step5data, data_length + 64, digest);
  
	/* Step 7 */
	for(i = 0; i < 64; i++)
	{
		hmac_sha1_variate.step7data[i] = hmac_sha1_variate.k0[i] ^ 0x5c;
	}

	/* Step 8 */
  memcpy(hmac_sha1_variate.step8data, hmac_sha1_variate.step7data, 64);
  memcpy(hmac_sha1_variate.step8data+64, digest, 20);

	/* Step 9 */
	sha1(hmac_sha1_variate.step8data, 64 + 20, digest);
}

typedef struct
{
  uint32_t n;
	int C1;
  int C2;
  int C3;
}base64_encode_variate_type;
static base64_encode_variate_type base64_encode_variate = {0};
static uint32_t base64_encode(char *dst, uint32_t dlen, const uint8_t *src, uint32_t slen)
{
  uint32_t i;
  char *p = (void *)0;
  
  memset(&base64_encode_variate, 0, sizeof(base64_encode_variate_type));
  
	if(slen == 0)
	{
		return 0;
	}

	base64_encode_variate.n = slen / 3 + (slen % 3 != 0);

	if(base64_encode_variate.n > (256 - 1) / 4)
	{
		return 0;
	}

	base64_encode_variate.n *= 4;

	if((dlen < base64_encode_variate.n + 1) || (NULL == dst))
	{
		return 0;
	}

	base64_encode_variate.n = (slen / 3) * 3;

	for(i = 0, p = dst; i < base64_encode_variate.n; i += 3)
	{
		base64_encode_variate.C1 = *src++;
		base64_encode_variate.C2 = *src++;
		base64_encode_variate.C3 = *src++;

		*p++ = base64_enc_map[(base64_encode_variate.C1 >> 2) & 0x3F];
		*p++ = base64_enc_map[(((base64_encode_variate.C1 &  3) << 4) + (base64_encode_variate.C2 >> 4)) & 0x3F];
		*p++ = base64_enc_map[(((base64_encode_variate.C2 & 15) << 2) + (base64_encode_variate.C3 >> 6)) & 0x3F];
		*p++ = base64_enc_map[base64_encode_variate.C3 & 0x3F];
	}

	if(i < slen)
	{
		base64_encode_variate.C1 = *src++;
		base64_encode_variate.C2 = ((i + 1) < slen) ? *src++ : 0;

		*p++ = base64_enc_map[(base64_encode_variate.C1 >> 2) & 0x3F];
		*p++ = base64_enc_map[(((base64_encode_variate.C1 & 3) << 4) + (base64_encode_variate.C2 >> 4)) & 0x3F];

		if((i + 1) < slen)
			*p++ = base64_enc_map[((base64_encode_variate.C2 & 15) << 2) & 0x3F];

		else *p++ = '=';

		*p++ = '=';
	}
  
	*p = 0;

	return (p - dst);
}

static char sign_t[40] = {0};
static uint8_t OTA_UrlEncode(char *sign)
{
	uint8_t i = 0, j = 0;
	uint8_t sign_len = strlen(sign);
	
	if(sign == (void *)0 || sign_len < 28) return 1;
	
  memset(sign_t, 0, sizeof(sign_t));
  
	for(; i<sign_len; i++)
	{
		sign_t[i] = sign[i];
		sign[i] = 0;
	}
	sign_t[i] = 0;
	
	for(i = 0, j = 0; i < sign_len; i++)
	{
		switch(sign_t[i])
		{
			case '+':
				strcat(sign + j, "%2B");j += 3;
			break;
			
			case ' ':
				strcat(sign + j, "%20");j += 3;
			break;
			
			case '/':
				strcat(sign + j, "%2F");j += 3;
			break;
			
			case '?':
				strcat(sign + j, "%3F");j += 3;
			break;
			
			case '%':
				strcat(sign + j, "%25");j += 3;
			break;
			
			case '#':
				strcat(sign + j, "%23");j += 3;
			break;
			
			case '&':
				strcat(sign + j, "%26");j += 3;
			break;
			
			case '=':
				strcat(sign + j, "%3D");j += 3;
			break;
			
			default:
				sign[j] = sign_t[i];j++;
			break;
		}
	}
	
	sign[j] = 0;
	
	return 0;
}

typedef struct
{
  char    dat[80];
  uint8_t hmacsha1[24];  /* sha1 输出长度固定为20byte */
  char    sign[88];      /* 经过base64编码后的长度为 [n/3]*4 经过url编码后 最大长度为原来的3倍  所以 28*3*/
}token_variate_type;
static token_variate_type token_variate = {0};

uint8_t Route_token(const char *did, char *out, unsigned short out_max_len)
{
  const char *ppid;
  const uint8_t *Pkey;
  
  if((did == NULL) || out == (void *)0 || out_max_len < 120)
    return 1;

	memset(&token_variate, 0, sizeof(token_variate_type));
  
  ppid = Route_str_pids[Route_DidToType(did)];
  Pkey = decode_ackey[Route_DidToType(did)];
  
  snprintf(token_variate.dat, sizeof(token_variate.dat), ""ROUTE_TOKEN_ET"\nsha1\nproducts/%s/devices/%s\n2018-10-31", ppid, did);
	
  hmac_sha1(Pkey, 32,
            token_variate.dat, strlen(token_variate.dat),
            token_variate.hmacsha1);
  
  base64_encode(token_variate.sign, sizeof(token_variate.sign), token_variate.hmacsha1, 20);
  
  OTA_UrlEncode(token_variate.sign);
  
  snprintf(out, out_max_len, "version=2018-10-31&res=products%%2F%s%%2Fdevices%%2F%s&et="ROUTE_TOKEN_ET"&method=sha1&sign=%s", ppid, did, token_variate.sign);
  
  return 0;
}
